#include "LoopBytes.h"

#include "LoopObj.c"

#define assert(x) if (!(x)) {printf("assert error: %s!\r\n", __FUNCTION__); while (1) {};}

#define debug //printf

//struct StLoopBytesMgr gLoopBytesMgr = {0};



struct StLoopBytesMgr * LoopBytesMgrInit(int max_bytes, int threshold_add, int threshold_get)
{

    struct StLoopBytesMgr *pMgr =  (struct StLoopBytesMgr *)malloc(sizeof(struct StLoopBytesMgr));
    memset(pMgr, 0, sizeof(struct StLoopBytesMgr));

    pMgr->flag_init = 1;

    LOCK_INIT(pMgr->lock);
    SEM_INIT(pMgr->sem_add, 0);
    SEM_INIT(pMgr->sem_get, 0);
    pMgr->pLoopObjMgr = CreateLoopObjMgr(sizeof(char), max_bytes, LOOP_MODE_BLOCKED);
    if (!pMgr->pLoopObjMgr) {
        printf("error: %s->CreateLoopObjMgr failed!\r\n", __FUNCTION__);
        return 0;
    }
    pMgr->bytes_max = pMgr->pLoopObjMgr->total_cnts;
    pMgr->threshold_add = threshold_add;
    pMgr->threshold_get = threshold_get;
    return pMgr;
}

int LoopBytesMgrCtrl(struct StLoopBytesMgr *pMgr, ELOOPBYTES_OPT opt, void *popt, int optlen)
{
    int ret = 0;
    //struct StLoopBytesMgr *pMgr =  &gLoopBytesMgr;
    assert(pMgr->pLoopObjMgr);
    LOCK(pMgr->lock);
    switch(opt)
    {
    case OPT_LOOPBYTES_GET_MAX:
        if (popt && optlen==4) {
            *(int*)popt = pMgr->bytes_max;
            debug("info: %s->set OPT_LOOPBYTES_GET_MAX=%d!\r\n", __FUNCTION__, *(int*)popt);
        }
        else {
            ret = -1;
        }
        break;
    case OPT_LOOPBYTES_SET_THRESHOLDADD:
        if (popt && optlen==4 && *(int*)popt <= pMgr->bytes_max && *(int*)popt >= 0) {
            pMgr->threshold_add = *(int*)popt;
            debug("info: %s->set OPT_LOOPBYTES_SET_THRESHOLDADD=%d!\r\n", __FUNCTION__, *(int*)popt);
        }
        break;
    case OPT_LOOPBYTES_SET_THRESHOLDGET:
        if (popt && optlen==4 && *(int*)popt <= pMgr->bytes_max && *(int*)popt >= 0) {
            pMgr->threshold_get = *(int*)popt;
            debug("info: %s->set OPT_THRESHOLDGET=%d!\r\n", __FUNCTION__, *(int*)popt);
        }
        break;
    case OPT_LOOPBYTES_SET_PUSH_MODE:
        if (popt && optlen==4) {
            pMgr->push_mode = *(int*)popt;
            debug("info: %s->set push_mode=%d!\r\n", __FUNCTION__, *(int*)popt);
        }
        break;
    case OPT_LOOPBYTES_SET_PULL_MODE:
        if (popt && optlen==4) {
            pMgr->pull_mode = *(int*)popt;
            debug("info: %s->set pull_mode=%d!\r\n", __FUNCTION__, *(int*)popt);
        }
        break;
    default:
        ret = -2;
        break;
    }
    UNLOCK(pMgr->lock);
    return ret;
}
int LoopBytesMgrPushPend(struct StLoopBytesMgr *pMgr, unsigned char *buf, int len, unsigned int timeout, int *err)
{
    int ret=0;
    int i=0;
    int error = 0;
    int pushed = 0;
    //struct StLoopBytesMgr *pMgr =  &gLoopBytesMgr;
    assert(pMgr->pLoopObjMgr);

    if (!err) err = &error;
    *err = 0;
    ///printf("info: %s, len=%d!\r\n", __FUNCTION__, len);
    LOCK(pMgr->lock);
    i = 0;
    while (i<len) {
        ret = PushLoopObjTail(pMgr->pLoopObjMgr, &buf[i]);
        if (ret < 0) {
            UNLOCK(pMgr->lock);
            SEM_POST(pMgr->sem_get);
            if (pMgr->push_mode == 1 && pushed > 0) {
                *err  = 0;
                goto END_FUN;
            }
            //printf("info: %s, (SET) SEM_PEND START, total=%d !\r\n", __FUNCTION__, pMgr->bytes_total);
            if (!SEM_PEND(pMgr->sem_add, timeout*1000)) {
                debug("warning:%s->SEM_PEND, (SET)  TIMEOUT, pushed=%d!\r\n", __FUNCTION__, pushed);
                *err  = -2;
                goto END_FUN;
            }
            //printf("info: %s, (SET) SEM_PEND END, total=%d !\r\n", __FUNCTION__, pMgr->bytes_total);
            LOCK(pMgr->lock);
        }
        else {
            pMgr->bytes_total++;
            pushed++;
            i++;
        }
    }
    UNLOCK(pMgr->lock);

END_FUN:
    if (pMgr->bytes_total >= pMgr->threshold_add) {
        //printf("info: %s, SEM_POST(sem_get)!\r\n", __FUNCTION__);
        SEM_POST(pMgr->sem_get);
    }
    return pushed;
}
int LoopBytesMgrPullPend(struct StLoopBytesMgr *pMgr, unsigned char *buf, int len, unsigned int timeout, int *err)
{
    int ret = 0;
    int i = 0;
    int error = 0;
    int pulled = 0;
    //struct StLoopBytesMgr *pMgr =  &gLoopBytesMgr;
    assert(pMgr->pLoopObjMgr);

    if (!err) err = &error;
    *err = 0;

    LOCK(pMgr->lock);
    i = 0;
    while (i<len) {
        ret = PullLoopObjHead(pMgr->pLoopObjMgr, &buf[i]);
        if (ret < 0) {
            UNLOCK(pMgr->lock);
            SEM_POST(pMgr->sem_add);
            if (pMgr->pull_mode == 1 && pulled > 0) { //mode == 1 and pulled more than one , return instantly
                *err = 0;
                goto END_FUN;
            }
            //printf("info: %s, (GET) SEM_PEND START, total=%d !\r\n", __FUNCTION__, pMgr->bytes_total);
            if (!SEM_PEND(pMgr->sem_get, timeout*1000)) {
                debug("info:%s->SEM_PEND, (GET) TIMEOUT, pulled=%d!\r\n", __FUNCTION__, pulled);
                *err  = -2;
                goto END_FUN;
            }
            //printf("info: %s, (GET) SEM_PEND END, total=%d !\r\n", __FUNCTION__, pMgr->bytes_total);
            LOCK(pMgr->lock);
        }
        else {
            pMgr->bytes_total--;
            pulled++;
            i++;
        }
    }
    UNLOCK(pMgr->lock);

END_FUN:

    if (pMgr->bytes_total <= pMgr->threshold_get)
    {
        SEM_POST(pMgr->sem_add);
    }
    return pulled;
}

int LoopBytesMgrPullPendPeek(struct StLoopBytesMgr *pMgr, unsigned char *buf, int len, unsigned int timeout, int *err)
{
    int ret = 0;
    int i = 0;
    int error = 0;
    int pulled = 0;
    //struct StLoopBytesMgr *pMgr =  &gLoopBytesMgr;
    assert(pMgr->pLoopObjMgr);

    if (!err) err = &error;
    *err = 0;

    LOCK(pMgr->lock);

    while (1) {
        ret = PrePullLoopObjHead(pMgr->pLoopObjMgr, buf, len);
        if (ret == len) break;
    }
    UNLOCK(pMgr->lock);

END_FUN:

    return pulled;
}



int LoopBytesMgrDeInit(struct StLoopBytesMgr *pMgr)
{
    if (pMgr==0) return 0;
    //struct StLoopBytesMgr *pMgr =  &gLoopBytesMgr;
    if (pMgr->pLoopObjMgr)
        DeleteLoopObjMgr(pMgr->pLoopObjMgr);
    SEM_DEINIT(pMgr->sem_add);
    SEM_DEINIT(pMgr->sem_get);
    LOCK_DEINIT(pMgr->lock);
    pMgr->flag_init = 0;
    pMgr->pull_mode = 0;
    pMgr->push_mode = 0;
    free(pMgr);
    return 0;
}

int LoopBytesMgrReset(struct StLoopBytesMgr *pMgr)
{
    //struct StLoopBytesMgr *pMgr =  &gLoopBytesMgr;
    LOCK(pMgr->lock);
    if (pMgr->pLoopObjMgr)
        ResetLoopObj(pMgr->pLoopObjMgr);
    pMgr->bytes_total = 0;
    UNLOCK(pMgr->lock);
    return 0;
}

int LoopBytesMgrTotals(struct StLoopBytesMgr *pMgr)
{
    //struct StLoopBytesMgr *pMgr =  &gLoopBytesMgr;
    if (pMgr->flag_init) {
        return pMgr->bytes_total;
    }
    return 0;
}

int LoopBytesMgrMax(struct StLoopBytesMgr *pMgr)
{
    //struct StLoopBytesMgr *pMgr =  &gLoopBytesMgr;
    if (pMgr->flag_init) {
        return pMgr->bytes_max;
    }
    return 0;
}

#if 0
int LoopBytesMgrTest()
{
    int ret = 0;
    int err = 0;
    char buf[100];
    unsigned int timeout = 0xFFFFFFFF;
    ret = LoopBytesMgrInit(LOOP_BYTES_MAX, 10 * 1024, 90 * 1024);
    char buf_x1[] = "hello world!";
    char buf_x2[] = "My Name is Vincent!";
    char buf_x3[] = "How do you do!";

    //ret = LoopBytesMgrPushPend(buf_x1, strlen(buf_x1), timeout, &err);
    //memset(buf, 0, sizeof(buf));
    //ret = LoopBytesMgrPullPend(buf, sizeof(buf), timeout, &err);
    ret = LoopBytesMgrPushPend(buf_x1, strlen(buf_x1), timeout, &err);
    ret = LoopBytesMgrPushPend(buf_x2, strlen(buf_x2), timeout, &err);
    ret = LoopBytesMgrPushPend(buf_x3, strlen(buf_x3), timeout, &err);
    memset(buf, 0, sizeof(buf));
    ret = LoopBytesMgrPullPend(buf, 1, timeout, &err);
    memset(buf, 0, sizeof(buf));
    ret = LoopBytesMgrPullPend(buf, 2, timeout, &err);
    memset(buf, 0, sizeof(buf));
    ret = LoopBytesMgrPullPend(buf, 3, timeout, &err);
    memset(buf, 0, sizeof(buf));
    ret = LoopBytesMgrPullPend(buf, 100, timeout, &err);


    LoopBytesMgrDeInit();
    return 0;
}

#endif

